/**
 * 1. Promise 是一个类，参数为一个执行器函数，且会被立刻执行
 * 2. Promise 有三种状态，初始状态为 pending
 *    - pending   等待
 *    - fulfilled 成功（padding => fulfilled）
 *    - rejected  失败（padding => rejected）
 *    状态一但变更 不可修改
 * 3. 使用 resolve 和 reject 函数改变 Promise 的状态
 *    - resolve() => fulfilled
 *    - reject()  => rejected
 * 4. then 函数内部判断当前状态
 *    - 当前状态为成功 调用成功回调函数 fulfilled => successCallback
 *    - 当前状态为失败 调用失败回调函数 rejected  => failCallback
 * 5. resolve 成功回调函数有一个参数，表示成功之后的值
 *    reject  失败回调函数有一个参数，表示失败后的原因
 * 6. 同一个 Promise 下面可以同时存在多个 then 方法，且会被顺序执行
 * 7. then 方法可以链式调用，后一个 then 方法的参数取决于前一个 then 方法的返回值
 */

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    // 执行器
    // NOTE 执行器函数将被立刻调用 所以创建 Promise 对象时执行器函数将同步执行
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }
  status = PENDING;
  value = undefined;
  reason = undefined;
  successCallback = [];
  failCallback = [];

  // 成功 pending => fulfilled
  // NOTE 使用箭头函数是为了使其始终指向当前 Promise 实例对象
  resolve = (value) => {
    // 如果状态不是等待 padding， 阻止程序向下执行
    if (this.status !== PENDING) {
      return;
    }
    // 状态改为成功 fulfilled
    this.status = FULFILLED;
    // 保存成功后的值
    this.value = value;
    // 调用成功回调函数
    while (this.successCallback.length) this.successCallback.shift()();
  };

  // 失败
  reject = (reason) => {
    // 如果状态不是等待 padding， 阻止程序向下执行
    if (this.status !== PENDING) {
      return;
    }
    // 状态改为失败 rejected
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 调用失败回调函数
    while (this.failCallback.length) this.failCallback.shift()();
  };

  then(successCallback, failCallback) {
    successCallback = successCallback || ((value) => value);
    failCallback = failCallback || ((error) => error);
    // 返回一个新的 Promise 对象，使其可以链式调用
    const promise2 = new MyPromise((resolve, reject) => {
      // 判断value值是否为Promise对象
      // 如果是，则继续调用then函数，成功调用resolve，失败调用reject
      // 否则直接返回
      const _tryCallback = (callback, callbackParams) => {
        // 转成异步任务 获取 promise2 的值
        setTimeout(() => {
          try {
            const callbackValue = callback(callbackParams);
            resolvePromise(promise2, callbackValue, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      };
      // 判断成功还是失败
      if (this.status === FULFILLED) {
        // 成功
        _tryCallback(successCallback, this.value);
      } else if (this.status === REJECTED) {
        // 失败
        _tryCallback(failCallback, this.reason);
      } else {
        // 等待
        // 将成功和失败回调函数存储起来，等待函数执行完毕以后调用
        this.successCallback.push(() => {
          _tryCallback(successCallback, this.value);
        });
        this.failCallback.push(() => {
          _tryCallback(failCallback, this.reason);
        });
      }
    });
    return promise2;
  }

  finally(callback) {
    return this.then(
      (value) => {
        // 此处即使向后传递 callback 也不会对后面的 then 函数有任何影响，因为不会作为其入参 且未被调用
        return MyPromise.resolve(
          typeof callback === 'function' ? callback() : callback
        ).then(() => value);
      },
      (error) => {
        return MyPromise.resolve(
          typeof callback === 'function' ? callback() : callback
        ).then(() => {
          throw error;
        });
      }
    );
  }

  catch(failCallback) {
    return this.then(undefined, failCallback);
  }

  static all(promiseArr) {
    let arr = [];
    let count = 1;
    return new MyPromise((resolve, reject) => {
      if (promiseArr instanceof Array) {
        for (let key in promiseArr) {
          const value = promiseArr[key];
          if (value instanceof MyPromise) {
            // Promise 对象
            value.then((res) => {
              arr[key] = res;
              // 统计成功的总数，如果值等于参数 promiseArr 数组长度
              // 说明 all 函数参数中所有的任务都执行完毕
              // 调用 resolve 函数 返回结果数组
              count++;
              if (count >= promiseArr.length) {
                resolve(arr);
              }
            }, reject);
          } else {
            // 普通值 直接返回
            arr[key] = value;
          }
        }
      }
    });
  }

  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    } else {
      return new MyPromise((resolve) => resolve(value));
    }
  }

  static reject(error) {
    if (error instanceof MyPromise) {
      return error;
    } else {
      return new MyPromise((resolve, reject) => reject(error));
    }
  }
}

function resolvePromise(currentPromise, callbackValue, resolve, reject) {
  if (currentPromise === callbackValue) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>')
    );
  }
  if (callbackValue instanceof MyPromise) {
    // Promise 对象
    callbackValue.then(resolve, reject);
  } else {
    // 普通值
    resolve(callbackValue);
  }
}

// export default MyPromise;
module.exports = MyPromise;
